﻿// <copyright file="FteOwnerRequirement.cs" company="Microsoft">
// Copyright © 2010 All Right Reserved
// </copyright>
// <author>Dmitry Kazantsev</author>
// <date>2010-01-23</date>
// <summary>Contains FteOwnerRequirement class</summary>

[assembly:System.CLSCompliant(false)]
namespace Microsoft.Msit.ActivityLibrary
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Reflection;
    using System.Workflow.Activities;
    using System.Workflow.ComponentModel;
    using Microsoft.ResourceManagement;
    using Microsoft.ResourceManagement.WebServices;
    using Microsoft.ResourceManagement.WebServices.WSResourceManagement;
    using Microsoft.ResourceManagement.Workflow.Activities;

    /// <summary>
    /// Initializes a new instance of the FteOwnerRequirement class
    /// </summary>
    public partial class FteOwnerRequirement : SequenceActivity, IDisposable
    {
        /// <summary>
        /// Field used to sore message for an Exception Event
        /// </summary>
        public const string AccessDenied = "Group must have at least one 'Full Time Employee' owner";

        /// <summary>
        /// Value representing URI of the Object Type allowed for this activity
        /// </summary>
        [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible", Justification = "By design")]
        public static DependencyProperty ObjectTypeNameProperty = DependencyProperty.Register(Constants.PropertyNameObjectType, typeof(string), typeof(FteOwnerRequirement));

        /// <summary>
        /// Field used for tracing within the activity
        /// </summary>
        private static TraceSource traceSource = new TraceSource("FteOwnerRequirementSource");

        /// <summary>
        /// Variable containing values representing identity of a current owner
        /// </summary>
        private Guid currentOwnerId;

        /// <summary>
        /// Field used for storing current index of enumerated owner
        /// </summary>
        private int currentOwnerIndex;

        /// <summary>
        /// The value represnting "Current Request" provided by the ILM
        /// </summary>
        private RequestType currentRequest;

        /// <summary>
        /// Field used for IDispose interface implementation
        /// </summary>
        private bool disposed;

        /// <summary>
        /// Field used to store a value representing whether current set of owners was "approved" or not
        /// </summary>
        private bool isApproved;

        /// <summary>
        /// Varaible containing value representing whether enumeration of all owners is completed or not
        /// </summary>
        private bool isEnumerated;

        /// <summary>
        /// Field used to store a value representing whether current group object is qualified (contains an FTE owner)
        /// </summary>
        private bool isQualified;

        /// <summary>
        /// Field used for collection owner GUIDs
        /// </summary>
        private Guid[] owners;

        /// <summary>
        /// Value representing ID of the request's target
        /// </summary>
        private Guid targetId;

        /// <summary>
        /// Initializes a new instance of the FteOwnerRequirement class
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1805:DoNotInitializeUnnecessarily")]
        public FteOwnerRequirement()
        {
            Trace(TraceEventType.Verbose, TraceActionType.ClassConstruciton, new StackFrame().GetMethod(), string.Empty);

            this.InitializeComponent();
            this.isEnumerated = false;
            this.isQualified = false;
            this.currentOwnerIndex = 0;
            this.currentOwnerId = Guid.Empty;
            this.targetId = Guid.Empty;
            this.owners = new Guid[] { Guid.Empty };
        }

        /// <summary>
        /// Custom Trace Log Events
        /// </summary>
        private enum TraceActionType
        {
            /// <summary>
            /// Entering the constructor of the class
            /// </summary>
            ClassConstruciton,

            /// <summary>
            /// Entering distructor of the class
            /// </summary>
            ClassDestruction,

            /// <summary>
            /// Entering property
            /// </summary>
            PropertyEntering,

            /// <summary>
            /// Leaving property
            /// </summary>
            PropertyLeaving,

            /// <summary>
            /// entering method
            /// </summary>
            MethodEntering,

            /// <summary>
            /// Executing method
            /// </summary>
            MethodExecution,

            /// <summary>
            /// Leaving method
            /// </summary>
            MethodLeaving,

            /// <summary>
            /// Begining a logical operation
            /// </summary>
            OperationBegin,

            /// <summary>
            /// Ending a logic operation
            /// </summary>
            OperationEnd,
        }

        /// <summary>
        /// Gets a GUID of a FIM Service account
        /// </summary>
        [Category("Properties")]
        public static Guid ServiceAccountId
        {
            get
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), ReservedAccounts.ServiceAccountId);
                return ReservedAccounts.ServiceAccountId;
            }
        }

        /// <summary>
        /// Gets or sets value representing identity of a current owner
        /// </summary>
        [Category("Properties")]
        public Guid CurrentOwnerId
        {
            get
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), this.currentOwnerId);
                return this.currentOwnerId;
            }

            set
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), value);
                this.currentOwnerId = value;
            }
        }

        /// <summary>
        /// Gets or sets value representing index of the currently processing owner
        /// </summary>
        [Category("Properties")]
        public int CurrentOwnerIndex
        {
            get
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), this.currentOwnerIndex);
                return this.currentOwnerIndex;
            }

            set
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), value);
                this.currentOwnerIndex = value;
            }
        }

        /// <summary>
        /// Gets or sets value of the currentRequest variable
        /// </summary>
        [Category("Properties")]
        public RequestType CurrentRequest
        {
            get
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), string.Empty);
                return this.currentRequest;
            }

            set
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), string.Empty);
                this.currentRequest = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether current group object was "approved"
        /// </summary>
        [Category("Properties")]
        public bool IsApproved
        {
            get
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), this.isApproved);
                return this.isApproved;
            }

            set
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), value);
                this.isApproved = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether enumeration of owners is completed
        /// </summary>
        [Category("Properties")]
        public bool IsEnumerated
        {
            get
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), this.isEnumerated);
                return this.isEnumerated;
            }

            set
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), value);
                this.isEnumerated = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether current request is qualified for this activity
        /// </summary>
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Category("Properties")]
        public bool IsQualified
        {
            get
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), this.isQualified);
                return this.isQualified;
            }

            set
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), value);
                this.isQualified = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether ObjectTypeName
        /// </summary>
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Properties")]
        public string ObjectTypeName
        {
            get
            {
                string value = (string)this.GetValue(FteOwnerRequirement.ObjectTypeNameProperty);
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), value);
                return value;
            }

            set
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), value);
                this.SetValue(FteOwnerRequirement.ObjectTypeNameProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets value representing collection of group owners
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "By design")]
        [Category("Properties")]
        public Guid[] Owners
        {
            get
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), string.Empty);
                return this.owners;
            }

            set
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), string.Empty);
                this.owners = value;
            }
        }

        /// <summary>
        /// Gets or sets value of the current target (group)
        /// </summary>
        [Category("Properties")]
        public Guid TargetId
        {
            get
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), this.targetId);
                return this.targetId;
            }

            set
            {
                Trace(TraceEventType.Verbose, TraceActionType.PropertyEntering, new StackFrame().GetMethod(), value);
                this.targetId = value;
            }
        }

        /// <summary>
        /// Disposes FteOwnerRequirement class
        /// </summary>
        /// <param name="disposing">State of disposal</param>
        protected override void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                Trace(TraceEventType.Verbose, TraceActionType.ClassDestruction, new StackFrame().GetMethod(), string.Empty);
                traceSource.Flush();
                traceSource.Close();
            }

            this.disposed = true;
        }


        /// <summary>
        /// Build a collection of owner identities
        /// </summary>
        /// <param name="ownersList">List of owners</param>
        /// <returns>Collection of owner identities</returns>
        private static Guid[] BuildIdentityCollection(object ownersList)
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            List<Guid> collection = new List<Guid>();

            if (null == ownersList)
            {
                Trace(TraceEventType.Warning, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), "Owner list is NULL. This could be an 'orphan' group");

                collection.TrimExcess();
                Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
                return collection.ToArray();
            }

            if (ownersList.GetType().IsArray)
            {
                object[] ownersIds = (object[])ownersList;

                foreach (object owner in ownersIds)
                {
                    if (owner.GetType() == typeof(Guid))
                    {
                        collection.Add((Guid)owner);
                    }
                }
            }
            else if (ownersList.GetType().Name.Equals("List`1", StringComparison.OrdinalIgnoreCase))
            {
                List<UniqueIdentifier> ownersIds = (List<UniqueIdentifier>)ownersList;

                foreach (UniqueIdentifier owner in ownersIds)
                {
                    if (owner.IsGuid)
                    {
                        collection.Add(new Guid(owner.Value));
                    }
                }
            }
            collection.TrimExcess();

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return collection.ToArray();
        }

        /// <summary>
        /// Combines two lists into one
        /// </summary>
        /// <param name="preExistingSet">collection of pre-existing owners</param>
        /// <param name="currentSet">collection of current owners</param>
        /// <returns>Combined collection of owners</returns>
        private static Guid[] CombineLists(Guid[] preExistingSet, Guid[] currentSet)
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            // Delete = we will have (some) duplicate IDs in PreExisting and in Current sets
            // Add = we will have only new (unique) records in Current set

            // Creating new list to store end-result and populating it with PreExisting list values
            List<Guid> list = new List<Guid>(preExistingSet);

            // Looping through pre-existing list (new list)
            foreach (Guid id in currentSet)
            {
                // If we have an ID in PreExisting list from Current list it's a delete
                if (list.Contains(id))
                {
                    // Removing that ID from the list
                    list.Remove(id);
                }
                else
                {
                    // Adding new unique ID to the end-result list
                    list.Add(id);
                }
            }

            list.TrimExcess();

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return list.ToArray();
        }

        /// <summary>
        /// Tracing helper
        /// </summary>
        /// <param name="eventType">An tracing event type</param>
        /// <param name="actionType">Tracing action type</param>
        /// <param name="methodBase">Method Base</param>
        /// <param name="value">Value</param>
        private static void Trace(TraceEventType eventType, TraceActionType actionType, MethodBase methodBase, object value)
        {
            // Converting value to string
            string payload = ValueToString(value);

            // Declaring 'message' varibale
            string message = string.Empty;

            switch (actionType)
            {

                case TraceActionType.ClassConstruciton:
                    {
                        message = string.Format(CultureInfo.InvariantCulture, "Initializing class {0}", methodBase.Name);
                        break;
                    }

                case TraceActionType.ClassDestruction:
                    {
                        message = string.Format(CultureInfo.InvariantCulture, "Destucting class {0}", methodBase.Name);
                        break;
                    }



                case TraceActionType.PropertyEntering:
                    {
                        if (string.IsNullOrEmpty(payload))
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Getting or setting [{0}] property", methodBase.Name);
                        }
                        else
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Getting or setting [{0}] property's value of '{1}'", methodBase.Name, payload);
                        }
                        break;
                    }
                case TraceActionType.PropertyLeaving:
                    {
                        if (string.IsNullOrEmpty(payload))
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Leaving [{0}] property", methodBase.Name);
                        }
                        else
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Leaving [{0}] property's with value '{1}'", methodBase.Name, payload);
                        }
                        break;
                    }


                case TraceActionType.MethodEntering:
                    {
                        if (string.IsNullOrEmpty(payload))
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Entering method [{0}]", methodBase.Name);
                        }
                        else
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Entering method [{0}]; {1}", methodBase.Name, payload);
                        }
                        break;
                    }

                case TraceActionType.MethodExecution:
                    {
                        if (string.IsNullOrEmpty(payload))
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Executing method [{0}]", methodBase.Name);
                        }
                        else
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Executing method [{0}]; {1}", methodBase.Name, payload);
                        }
                        break;
                    }

                case TraceActionType.MethodLeaving:
                    {
                        if (string.IsNullOrEmpty(payload))
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Leaving Method [{0}]", methodBase.Name);
                        }
                        else
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Leaving Method [{0}]; {1}", methodBase.Name, payload);
                        }
                        break;
                    }

                case TraceActionType.OperationBegin:
                    {
                        if (string.IsNullOrEmpty(payload))
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Method [{0}] begining to execute operation", methodBase.Name);
                        }
                        else
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Method [{0}] begining to execute operation '{1}'", methodBase.Name, payload);
                        }
                        break;
                    }

                case TraceActionType.OperationEnd:
                    {
                        if (string.IsNullOrEmpty(payload))
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Method [{0}] finishing execution of operation", methodBase.Name);
                        }
                        else
                        {
                            message = string.Format(CultureInfo.InvariantCulture, "Method [{0}] finishing execution of operation '{1}'", methodBase.Name, payload);
                        }
                        break;
                    }


                default:
                    {
                        break;
                    }
            }

            int eventId = (int)eventType + (int)actionType;

            traceSource.TraceEvent(eventType, eventId, message);
        }

        /// <summary>
        /// Converts provided value into string
        /// </summary>
        /// <param name="value">Value to be converted</param>
        /// <returns>String representation of the value</returns>
        private static string ValueToString(object value)
        {
            string result = string.Empty;

            if (null == value)
            {
                return result;
            }

            Type currentType = value.GetType();

            switch (currentType.Name.ToUpperInvariant())
            {
                case "GUID":
                    {
                        Guid payload = (Guid)value;
                        result = payload.ToString("D", CultureInfo.InvariantCulture);
                        break;
                    }
                case "STRING":
                    {
                        result = (string)value;
                        break;
                    }
                case "BOOLEAN":
                    {
                        bool payload = (bool)value;
                        result = payload.ToString(CultureInfo.InvariantCulture);
                        break;
                    }
                case "INT32":
                    {
                        int payload = (int)value;
                        result = payload.ToString(CultureInfo.InvariantCulture);
                        break;
                    }
                default:
                    {
                        break;
                    }
            }

            return result;
        }

        /// <summary>
        /// Adds anumerated owners
        /// </summary>
        /// <param name="sender">Method that triggered an event</param>
        /// <param name="arguments">Passed arguments</param>
        private void ExecuteCodeAddEnumerateOwners(object sender, EventArgs arguments)
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            object[] currentOperationOwnerSet = (object[])this.GetAttributeValueFromCurrentRequest("Owner");
            Guid[] currentOwners = BuildIdentityCollection(currentOperationOwnerSet);

            this.Owners = currentOwners;

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
        }

        /// <summary>
        /// Assignes owner to an read resource activity
        /// </summary>
        /// <param name="sender">Method that triggered an event</param>
        /// <param name="arguments">Passed arguments</param>
        private void ExecuteCodeAssignCurrentOwner(object sender, EventArgs arguments)
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Checking whether collection of 'owners' is lesser than current owner index");
            if (this.Owners.Length > this.CurrentOwnerIndex)
            {
                this.CurrentOwnerId = this.Owners[this.CurrentOwnerIndex];
                this.CurrentOwnerIndex++;
                Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), string.Format(CultureInfo.InvariantCulture, "Current Owner Index is updated to: {0}", this.CurrentOwnerIndex.ToString(CultureInfo.InvariantCulture)));
            }
            else
            {
                this.IsEnumerated = true;
            }

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
        }

        /// <summary>
        /// Parses values of current request object
        /// </summary>
        /// <param name="sender">An object requested this operation</param>
        /// <param name="arguments">Additional parameters sent along with an object</param>
        private void ExecuteCodeGetTargetId(object sender, EventArgs arguments)
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            if (this.CurrentRequest.Target.IsGuid)
            {
                this.TargetId = new Guid(this.CurrentRequest.Target.Value);
            }
            else
            {
                const string Message = "Cannot read Target ID";
                Trace(TraceEventType.Critical, TraceActionType.MethodExecution, new StackFrame().GetMethod(), Message);
                throw new IdentityManagementException(Message);
            }

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender">Method that triggered an event</param>
        /// <param name="arguments">Passed arguments</param>
        private void ExecuteCodeModifyEnumerateOwners(object sender, EventArgs arguments)
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            List<UniqueIdentifier> preExistingOwnerSet = (List<UniqueIdentifier>)this.readResourceTargetGroup.Resource["Owner"];
            object[] currentOperationOwnerSet = (object[])this.GetAttributeValueFromCurrentRequest("Owner");

            Guid[] preExistingOwners = BuildIdentityCollection(preExistingOwnerSet);
            Guid[] currentOwners = BuildIdentityCollection(currentOperationOwnerSet);

            this.Owners = CombineLists(preExistingOwners, currentOwners);

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
        }

        /// <summary>
        /// Validates current request
        /// </summary>
        /// <param name="sender">An object requested this operation</param>
        /// <param name="arguments">Additional parameters sent along with an object</param>
        private void ExecuteCodeValidateRequestActivity(object sender, EventArgs arguments)
        {
            // Verifying current request object for conditions that persist in all configurations
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            // Verifying parent workflow
            if (!this.VerifyParentWorkflow()) return;

            // Verify current request presence
            if (!this.VerifyCurrentRequest()) return;

            // Verify current object type to be specified/available in the request
            if (!this.VerifyObjectTypePresence()) return;

            // Verify whether current object type is the object type specified by administrator in UI
            if (!this.VerifyObjectTypeName()) return;

            // Verify Request Status Type
            if (!this.VerifyRequestStatusType()) return;

            Trace(TraceEventType.Information, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Current request parameters are qualified for this activity to execute normally");
            this.IsQualified = true;

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender">Method that triggered an event</param>
        /// <param name="arguments">Passed arguments</param>
        private void ExecuteCodeVerifyFte(object sender, EventArgs argiments)
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            try
            {
                Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Accessing current instance of ExecuteCodeVerifyFte activity");
                CodeActivity currentInstanceCodeActivityInstance = sender as CodeActivity;

                Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Accessing current instance of readResourceCurrentOwner activity");
                ReadResourceActivity currentReadResourceActivityInstance = (ReadResourceActivity)currentInstanceCodeActivityInstance.Parent.Activities["readResourceCurrentOwner"];

                Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Reading value of [EmployeeType] attribute");
                string ownerEmployeeType = currentReadResourceActivityInstance.Resource["EmployeeType"] as string;

                Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), string.Format(CultureInfo.InvariantCulture, "[EmployeeType] value is: '{0}'", ownerEmployeeType));
                Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Checking whether current employee type is 'Full Time Employee'");
                if (ownerEmployeeType.Equals("Full Time Employee", StringComparison.OrdinalIgnoreCase))
                {
                    Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Current employee type is 'Full Time Employee'");
                    Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Setting [IsApproved] and [IsEnumerated] to TRUE");
                    this.IsApproved = true;
                    this.IsEnumerated = true;
                }
                else
                {
                    Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Current employee type is not 'Full Time Employee'. Moving to the next owner.");
                }
            }
            catch (NullReferenceException)
            {
                Trace(TraceEventType.Critical, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Cannot read values in activity. Moving to the next object/activity instance");
                Trace(TraceEventType.Critical, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Setting IsEnumerated to TRUE. This will abandon enumeration");
                this.IsEnumerated = true;
            }

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
        }

        /// <summary>
        /// Gets a first found value of the attribute in question from current request object
        /// </summary>
        /// <param name="attributeName">Name of the attribute in question</param>
        /// <returns>Value of the attribute in question</returns>
        private object GetAttributeValueFromCurrentRequest(string attributeName)
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            List<object> values = new List<object>();

            ReadOnlyCollection<CreateRequestParameter> requestParameters = this.CurrentRequest.ParseParameters<CreateRequestParameter>();

            for (int i = 0; i < requestParameters.Count; i++)
            {
                if (requestParameters[i].PropertyName.Equals(attributeName, StringComparison.OrdinalIgnoreCase))
                {
                    values.Add(requestParameters[i].Value);
                }
            }

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return values.ToArray();
        }

        /// <summary>
        /// Gets an array representing collection of names for object types
        /// </summary>
        /// <returns>collection of object types</returns>
        private string[] ObjectTypes()
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);

            List<string> collection = new List<string>();
            if (string.IsNullOrEmpty(this.ObjectTypeName))
            {
                collection.TrimExcess();
                return collection.ToArray();
            }

            string[] values = this.ObjectTypeName.Split(new char[] { ';' });

            foreach (string value in values)
            {
                if (!string.IsNullOrEmpty(value.Trim()))
                {
                    collection.Add(value.Trim());
                }
            }

            collection.TrimExcess();

            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return collection.ToArray();

        }

        /// <summary>
        /// Verifies existance of Current Request object
        /// </summary>
        /// <returns>True or false</returns>
        private bool VerifyCurrentRequest()
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);
            Trace(TraceEventType.Verbose, TraceActionType.OperationBegin, new StackFrame().GetMethod(), "Checking whether Current Request is not NULL");
            if (null == this.CurrentRequest)
            {
                Trace(TraceEventType.Error, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Cannot access Current Request. Disqualifying activity from execution");
                Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), "Checking whether Current Request is not NULL");
                Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
                return false;

            }
            Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), "Checking whether Current Request is not NULL");
            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return true;
        }

        /// <summary>
        /// Verifies whether current request contains object type specified by Administrator in UI
        /// </summary>
        /// <returns>True or False</returns>
        private bool VerifyObjectTypeName()
        {
            //// Request object type must be one of the object specified in the UI
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);
            Trace(TraceEventType.Verbose, TraceActionType.OperationBegin, new StackFrame().GetMethod(), @"Verifying whether 'TargetObjectType' in the list of administrator-specified types");

            bool isAllowedObjectType = false;
            Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Looping through specified object types");
            foreach (string objectType in this.ObjectTypes())
            {
                if (this.CurrentRequest.TargetObjectType.Equals(objectType, StringComparison.OrdinalIgnoreCase))
                {
                    string message = string.Format(CultureInfo.InvariantCulture, "Current object type is: '{0}'; administrator-specified object tpye is: '{1}'", this.CurrentRequest.TargetObjectType, objectType);
                    Trace(TraceEventType.Verbose, TraceActionType.MethodExecution, new StackFrame().GetMethod(), message);
                    isAllowedObjectType = true;

                    Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), @"Verifying whether 'TargetObjectType' in the list of administrator-specified types");
                    break;
                }
            }

            if (false == isAllowedObjectType)
            {
                Trace(TraceEventType.Error, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "'Current' and 'Specified' object types are NOT the same. Disqualifying this request");
                Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), @"Verifying whether 'TargetObjectType' in the list of administrator-specified types");
                Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
                return false;
            }

            Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), @"Verifying whether 'TargetObjectType' in the list of administrator-specified types");
            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return true;
        }

        /// <summary>
        /// Verifies Object Type to be present and readable in Current Request
        /// </summary>
        /// <returns>True or false</returns>
        private bool VerifyObjectTypePresence()
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);
            Trace(TraceEventType.Verbose, TraceActionType.OperationBegin, new StackFrame().GetMethod(), @"Verifying whether 'TargetObjectType' is null or empty string");
            if (string.IsNullOrEmpty(this.CurrentRequest.TargetObjectType))
            {
                Trace(TraceEventType.Error, TraceActionType.MethodExecution, new StackFrame().GetMethod(), @"Cannot access TargetObjectType. Disqualifying activity from execution");
                Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), @"Verifying whether 'TargetObjectType' is null or empty string");
                Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
                return false;
            }
            Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), @"Verifying whether 'TargetObjectType' is null or empty string");
            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return true;
        }

        /// <summary>
        /// Verifies whether Operation Type is 'Create' or 'Put' type
        /// </summary>
        /// <returns>True or false</returns>
        private bool VerifyOperationType()
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);
            Trace(TraceEventType.Verbose, TraceActionType.OperationBegin, new StackFrame().GetMethod(), @"Verifying whether current operation if 'Create' (creation of a new object) or 'Put' (modification of existing object)");
            if (this.CurrentRequest.Operation != OperationType.Create && this.CurrentRequest.Operation != OperationType.Put)
            {
                Trace(TraceEventType.Error, TraceActionType.MethodExecution, new StackFrame().GetMethod(), @"Cannot access TargetObjectType. Disqualifying current request from execution");
                Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), @"Verifying whether current operation if 'Create' (creation of a new object) or 'Put' (modification of existing object)");
                Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
                return false;
            }
            Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), @"Verifying whether current operation if 'Create' (creation of a new object) or 'Put' (modification of existing object)");
            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return true;
        }

        /// <summary>
        /// Verifies that current request contains "Parent Workflow"
        /// </summary>
        /// <returns>True or false</returns>
        private bool VerifyParentWorkflow()
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);
            Trace(TraceEventType.Verbose, TraceActionType.OperationBegin, new StackFrame().GetMethod(), "Accessing parent workflow");
            SequentialWorkflow parentWorkflow = null;
            if (!SequentialWorkflow.TryGetContainingWorkflow(this, out parentWorkflow))
            {
                Trace(TraceEventType.Error, TraceActionType.MethodExecution, new StackFrame().GetMethod(), "Cannot access parent workflow. Disqualifying activity from execution");
                Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), "Accessing parent workflow");
                Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
                return false;
            }
            Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), "Accessing parent workflow");
            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return true;
        }

        /// <summary>
        /// Verifies whether Request Status Type is 'Authorized', 'Authorizing', 'Validating' or 'Validated'
        /// </summary>
        /// <returns>True or false</returns>
        private bool VerifyRequestStatusType()
        {
            Trace(TraceEventType.Information, TraceActionType.MethodEntering, new StackFrame().GetMethod(), string.Empty);
            Trace(TraceEventType.Verbose, TraceActionType.OperationBegin, new StackFrame().GetMethod(), @"Verifying whether Request Status Type is 'Authorized', 'Authorizing', 'Validating' or 'Validated'");

            RequestStatusType currentStatus = this.CurrentRequest.RequestStatus;
            if (currentStatus != RequestStatusType.Authorized && currentStatus != RequestStatusType.Authorizing && currentStatus != RequestStatusType.Validating && currentStatus != RequestStatusType.Validated)
            {
                Trace(TraceEventType.Error, TraceActionType.MethodExecution, new StackFrame().GetMethod(), string.Format(CultureInfo.InvariantCulture, "Current status is '{0}'. Disqualifying current request from execution", Enum.GetName(typeof(RequestStatusType), currentStatus)));
                Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), @"Verifying whether Request Status Type is 'Authorized', 'Authorizing', 'Validating' or 'Validated'");
                Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
                return false;
            }
            Trace(TraceEventType.Verbose, TraceActionType.OperationEnd, new StackFrame().GetMethod(), @"Verifying whether Request Status Type is 'Authorized', 'Authorizing', 'Validating' or 'Validated'");
            Trace(TraceEventType.Verbose, TraceActionType.MethodLeaving, new StackFrame().GetMethod(), string.Empty);
            return true;
        }

        /// <summary>
        /// Struct containing special/reserved accounts 
        /// </summary>
        private struct ReservedAccounts
        {
            /// <summary>
            /// The GUID value of Anonymous user account
            /// </summary>
            internal static readonly Guid AnonymousId = new Guid("b0b36673-d43b-4cfa-a7a2-aff14fd90522");

            /// <summary>
            /// The GUID value of Sync Service account
            /// </summary>
            internal static readonly Guid SyncAccountId = new Guid("FB89AEFA-5EA1-47f1-8890-ABE7797D6497");

            /// <summary>
            /// The GUID value of FIM Service account
            /// </summary>
            internal static Guid ServiceAccountId = new Guid("E05D1F1B-3D5E-4014-BAA6-94DEE7D68C89");
        }

    }
}
